/*
	Written by: Daniel Mathis
	
	Implements non-trivial methods for GameObjects
*/

#include "GameObjects.h"

/* ------------------------------------------------------------------------ 
		SHIP STUFF
------------------------------------------------------------------------ */

Ship::Ship(const GLfloat newWidth, const GLfloat newHeight, GLfloat newXLoc, GLfloat newYLoc)
:	width(newWidth), height(newHeight), radius(max(width/2, height/2)), xLoc(newXLoc), yLoc(newYLoc),
	xVelocity(0), yVelocity(0), accelerating(false), decelerating(false)
{}

void Ship::draw()
{
	GLfloat topLeftX, topRightX, bottomLeftX, bottomRightX,
			topLeftY, topRightY, bottomLeftY, bottomRightY;
	topLeftX = -width/2;
	bottomLeftX = topLeftX;
	topRightX = topLeftX + width;
	bottomRightX = topRightX;
	bottomLeftY = -height/2;
	bottomRightY = bottomLeftY;
	topLeftY = bottomLeftY + height;
	topRightY = topLeftY;
	
	glLoadIdentity();
	glTranslatef(xLoc, yLoc, 0.0);
	glRotatef(direction*57.29577, 0.0 , 0.0 , 1.0);
	glBegin(GL_QUADS);
		glColor3f(1.0, 1.0, 1.0);
		glVertex3f(topLeftX, topLeftY, SHIPZ);
		glVertex3f(bottomLeftX, bottomLeftY, SHIPZ);
		glVertex3f(bottomRightX, bottomRightY, SHIPZ);
		glVertex3f(topRightX, topRightY, SHIPZ);
	glEnd();
	glLoadIdentity();
}

void Ship::move()
{
	// Change the ship's direction based on whether it's turning
	if(turningRight)
	{
		direction -= TURNAMOUNT;
		if(direction < 0)
			direction = 6.27;
	}
	if(turningLeft)
	{
		direction += TURNAMOUNT;
		if(direction > 6.2831)
			direction = 0.1;
	}
	// Change the ship's velocity based on whether it's currently accelerating
	if(accelerating)
	{
		xVelocity += SHIPACC*cos(direction);
		yVelocity += SHIPACC*sin(direction);
	}
	if(decelerating)
	{
		xVelocity -= SHIPACC*cos(direction);
		yVelocity -= SHIPACC*sin(direction);
	}
	// Change the ship's position based on its current velocity
	xLoc = fmod((xLoc + xVelocity),WIDTH);
	if(xLoc < 0)
		xLoc = WIDTH; 
	yLoc = fmod((yLoc + yVelocity),HEIGHT);
	if(yLoc < 0)
		yLoc = HEIGHT;
}

/* ------------------------------------------------------------------------ 
		SHIP CONTROLLER STUFF
------------------------------------------------------------------------ */

void ShipController::generateShip(const GLfloat newWidth, const GLfloat newHeight, GLfloat newXLoc, GLfloat newYLoc)
{
	shipObjects.push_back(new Ship(newWidth, newHeight, newXLoc, newYLoc));
}

void ShipController::deleteShip()
{
	shipObjects.clear();
}

void ShipController::draw()
{
	if(shipObjects.size() > 0)
		shipObjects[0]->draw();
}

void ShipController::move()
{
	if(shipObjects.size() > 0)
		shipObjects[0]->move();
}

/* ------------------------------------------------------------------------ 
		ASTEROID STUFF
------------------------------------------------------------------------ */

Asteroid::Asteroid(const GLfloat newWidth, const GLfloat newHeight, GLfloat newXLoc, GLfloat newYLoc, GLfloat initXVelocity, GLfloat initYVelocity)
:	width(newWidth), height(newHeight), radius(max(width/2, height/2)), xLoc(newXLoc), yLoc(newYLoc),
	xVelocity(initXVelocity), yVelocity(initYVelocity), accelerating(false), decelerating(false)
{}

void Asteroid::draw()
{
	GLfloat topLeftX, topRightX, bottomLeftX, bottomRightX,
			topLeftY, topRightY, bottomLeftY, bottomRightY;
		topLeftX = -width/2;
	bottomLeftX = topLeftX;
	topRightX = topLeftX + width;
	bottomRightX = topRightX;
	bottomLeftY = -height/2;
	bottomRightY = bottomLeftY;
	topLeftY = bottomLeftY + height;
	topRightY = topLeftY;
	
	glLoadIdentity();
	glTranslatef(xLoc, yLoc, 0.0);
	glBegin(GL_QUADS);
		glColor3f(1.0, 0.0, 0.0);
		glVertex3f(topLeftX, topLeftY, ASTEROIDZ);
		glVertex3f(bottomLeftX, bottomLeftY, ASTEROIDZ);
		glVertex3f(bottomRightX, bottomRightY, ASTEROIDZ);
		glVertex3f(topRightX, topRightY, SHIPZ);
	glEnd();
	glLoadIdentity();
}

void Asteroid::move()
{
	// Change the asteroid's velocity based on whether it's currently accelerating
	if(accelerating)
	{
		xVelocity += ASTEROIDACC;
		yVelocity += ASTEROIDACC;
	}
	if(decelerating)
	{
		xVelocity -= ASTEROIDACC;
		yVelocity -= ASTEROIDACC;
	}
	// Change the ship's position based on its current velocity
	xLoc = fmod((xLoc + xVelocity),WIDTH);
	if(xLoc < 0)
		xLoc = WIDTH; 
	yLoc = fmod((yLoc + yVelocity),HEIGHT);
	if(yLoc < 0)
		yLoc = HEIGHT;
}

/* ------------------------------------------------------------------------ 
		ASTEROID CONTROLLER STUFF
------------------------------------------------------------------------ */

// maxWidth and maxHeight of an asteroid must be > 10. 
void AsteroidController::generateAsteroids(const int numberOfAsteroids, GLfloat maxWidth, GLfloat maxHeight, const GLfloat minVelocity, const GLfloat maxVelocity)
{
	// Reset to lowest allowed values if maxWidth or maxHeight are too small
	if(maxWidth < ASTEROIDMINWIDTH)
		maxWidth = ASTEROIDMINWIDTH+1;
	if(maxHeight < ASTEROIDMINHEIGHT)
		maxHeight = ASTEROIDMINHEIGHT+1;
	// Generate asteroids and add them to the end of the gameObjects vector
	for(int i = 0; i < numberOfAsteroids; i++)
	{
		int xSign, ySign;
		// Decide whether x and y velocities are negative or positive
		if(rand() % 2 == 0)
			xSign = -1;
		else
			xSign = 1;
		if(rand() % 2 == 0)
			ySign = -1;
		else
			ySign = 1;
		asteroidObjects.push_back(new Asteroid(fmod(rand(),(maxWidth - 10)) + 10, fmod(rand(),(maxHeight - 10)) + 10, fmod(rand(), WIDTH), fmod(rand(),HEIGHT), xSign*(fmod(rand(),maxVelocity) + minVelocity), ySign*(fmod(rand(),maxVelocity) + minVelocity)));
	}
}

void AsteroidController:: deleteAsteroids()
{
	// Remove all except the first element from gameObjects
	asteroidObjects.clear();
}

void AsteroidController::draw()
{
	// Iterate through each asteroid, calling their draw() function
	list<Asteroid*>::iterator it;
	
	for(it = asteroidObjects.begin(); it != asteroidObjects.end(); ++it)
		(*it)->draw();
}

void AsteroidController::move()
{
	// Iterate through each asteroid, calling their move() function
	list<Asteroid*>::iterator it;
	
	for(it = asteroidObjects.begin(); it != asteroidObjects.end(); ++it)
		(*it)->move();
}

/* ------------------------------------------------------------------------ 
		BULLET STUFF
------------------------------------------------------------------------ */

Bullet::Bullet(const GLfloat newXLoc, const GLfloat newYLoc, const GLfloat newDirection, const GLfloat newWidth, const GLfloat newHeight, const GLfloat initXVelocity, const GLfloat initYVelocity)
: width(newWidth), height(newHeight), radius(max(width, height)), xLoc(newXLoc), yLoc(newYLoc), xVelocity(initXVelocity), yVelocity(initYVelocity),
  accelerating(false), decelerating(false), direction(newDirection), lifeTime(0)
{}

void Bullet::draw()
{
	GLfloat topLeftX, topRightX, bottomLeftX, bottomRightX,
			topLeftY, topRightY, bottomLeftY, bottomRightY;
	topLeftX = -width/2;
	bottomLeftX = topLeftX;
	topRightX = topLeftX + width;
	bottomRightX = topRightX;
	bottomLeftY = -height/2;
	bottomRightY = bottomLeftY;
	topLeftY = bottomLeftY + height;
	topRightY = topLeftY;
	
	glLoadIdentity();
	glTranslatef(xLoc, yLoc, 0.0);
	glRotatef(direction*57.29577, 0.0 , 0.0 , 1.0);
	glBegin(GL_QUADS);
		glColor3f(1.0, 1.0, 1.0);
		glVertex3f(topLeftX, topLeftY, BULLETZ);
		glVertex3f(bottomLeftX, bottomLeftY, BULLETZ);
		glVertex3f(bottomRightX, bottomRightY, BULLETZ);
		glVertex3f(topRightX, topRightY, BULLETZ);
	glEnd();
	glLoadIdentity();
}


void Bullet::move()
{
	// Change the ship's velocity based on whether it's currently accelerating
	if(accelerating)
	{
		xVelocity += BULLETACC*cos(direction);
		yVelocity += BULLETACC*sin(direction);
	}
	if(decelerating)
	{
		xVelocity -= BULLETACC*cos(direction);
		yVelocity -= BULLETACC*sin(direction);
	}
	// Change the ship's position based on its current velocity
	xLoc = fmod(xLoc + xVelocity*cos(direction),WIDTH);
	if(xLoc < 0)
		xLoc = WIDTH; 
	yLoc = fmod(yLoc + yVelocity*sin(direction),HEIGHT);
	if(yLoc < 0)
		yLoc = HEIGHT;
		
	// Update the bullet's lifeTime
	lifeTime++;
}

/* ------------------------------------------------------------------------ 
		BULLET CONTROLLER STUFF
------------------------------------------------------------------------ */

void BulletController::generateBullet(const GLfloat newXLoc, const GLfloat newYLoc, const GLfloat newDirection, const GLfloat newWidth, const GLfloat newHeight, const GLfloat initXVelocity, const GLfloat initYVelocity)
{
	bulletObjects.push_back(new Bullet(newXLoc, newYLoc, newDirection, newWidth, newHeight, initXVelocity, initYVelocity));
}

void BulletController::deleteBullets()
{
	bulletObjects.clear();
}

void BulletController::draw()
{
	// Iterate through each bullet, calling their draw() function
	list<Bullet*>::iterator it;
	
	for(it = bulletObjects.begin(); it != bulletObjects.end(); ++it)
		(*it)->draw();
}

void BulletController::move()
{
	// Iterate through each bullet, calling their move() function
	list<Bullet*>::iterator it;
	// After the move, check whether it has lived too long. If it's lived longer than BULLETLIFETIME, remove the bullet.
	for(it = bulletObjects.begin(); it != bulletObjects.end(); )
	{
		(*it)->move();
		if((*it)->itsLifeTime() > BULLETLIFETIME)
			it = bulletObjects.erase(it);
		else
			++it;
	}
}
